﻿// -----------------------------------------------------------------------
// <copyright file="ApplicationOfSortAndMaxAndMin.cs" company="fcis">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------

namespace IP_Package.Operations.Restoration.RemoveNoise
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Linq;
    using System.Text;

    /// <summary>
    /// TODO: Update summary.
    /// </summary>
    public class ApplicationOfSortAndMaxAndMin
    {
        /// <summary>
        /// min value in array
        /// </summary>
        private MixedColor min;

      /// <summary>
      /// max value in array
      /// </summary>
        private MixedColor max;

         /// <summary>
        /// Calculates the bit-mixing value ki for color c .
        /// </summary>
        /// <param name="c">The color byte.</param>
        /// <returns>bit-mixing value</returns>
        public int CalculateMixedColor(Color c)
        {
            string red = Convert.ToString(c.R, 2);
            string blue = Convert.ToString(c.B, 2);
            string green = Convert.ToString(c.G, 2);
            int redLen = red.Length;
            int blueLen = blue.Length;
            int greenLen = green.Length;
            for (int i = 0; i < (8 - redLen); i++) 
            {
                red = "0" + red;
            }

            for (int i = 0; i < (8 - greenLen); i++)
            {
                green = "0" + green;
            }

            for (int i = 0; i < (8 - blueLen); i++)
            {
                blue = "0" + blue;
            }

            string k = string.Empty;
            for (int i = 0; i < 8; i++)
            {
                k = k.ToString() + red[i].ToString() + green[i].ToString() + blue[i].ToString();
            }

            return Convert.ToInt32(k, 2);
        }

        /// <summary>
        /// Gets the value of type require.
        /// </summary>
        /// <param name="window">The window of values.</param>
        /// <param name="type">The type of operation.</param>
        /// <returns>array of result</returns>
        public MixedColor[] GetValue(Color[,] window, string type)
        {
            MixedColor[] mixValueOfColors = new MixedColor[window.Length];
            int index = 0;
            for (int i = 0; i < window.GetLength(0); i++)
            {
                for (int j = 0; j < window.GetLength(0); j++)
                {
                    MixedColor pxl;
                    pxl.X = i;
                    pxl.Y = j;
                    pxl.Value = this.CalculateMixedColor(window[i, j]);
                    mixValueOfColors[index] = pxl;
                  index++;
                }
            }

            MixedColor[] returnedValue = new MixedColor[1];
            switch (type)
            {
                case "Median":
                    {
                        returnedValue = new MixedColor[1];
                        returnedValue[0] = this.GetMedian(mixValueOfColors);
                    }

                    break;
                case "Min":
                    {
                        returnedValue = new MixedColor[1];
                        returnedValue[0] = this.GetMin(mixValueOfColors);
                    }

                    break;
                case "Max":
                    {
                        returnedValue = new MixedColor[1];
                        returnedValue[0] = this.Getmax(mixValueOfColors);
                    }

                    break;
                case "Midpoint":
                    {
                        returnedValue = new MixedColor[2];
                        this.GetMidPoint(mixValueOfColors, returnedValue);
                    }

                    break;
                default:
                    break;
            }

            return returnedValue;
        }

        /// <summary>
        /// Get max of array.
        /// </summary>
        /// <param name="value">The array of values.</param>
        /// <returns>the value of max in array</returns>
        public MixedColor Getmax(MixedColor[] value)
        {
            this.max = value[0];
            for (int i = 1; i < value.Length; i++)
            {
                if (value[i].Value > this.max.Value)
                {
                    this.max = value[i];
                }
            }

                return this.max;
        }

        /// <summary>
        /// Gets the min in array.
        /// </summary>
        /// <param name="value">The array of value.</param>
        /// <returns>value of min in array</returns>
        public MixedColor GetMin(MixedColor[] value)
        {
            this.min = value[0];
            for (int i = 1; i < value.Length; i++)
            {
                if (value[i].Value < this.min.Value)
                {
                    this.min = value[i];
                }
            }

            return this.min;
        }

        /// <summary>
        /// Gets the min and max in array.
        /// </summary>
        /// <param name="value">The array of values.</param>
        public void GetMinAndMax(MixedColor[] value)
        {
             this.min = value[0];
             this.max = value[0];
             for (int i = 1; i < value.Length - 1; i = +2)
             {
                 if (value[i].Value < value[i + 1].Value)
                 {
                     if (value[i].Value < this.min.Value)
                     {
                         this.min = value[i];
                     }

                     if (value[i + 1].Value > this.max.Value)
                     {
                         this.max = value[i + 1];
                     }
                 }
                 else
                 {
                     if (value[i + 1].Value < this.min.Value)
                     {
                         this.min = value[i + 1];
                     }

                     if (value[i].Value > this.max.Value)
                     {
                         this.max = value[i];
                     }
                 }
             }
        }

        /// <summary>
        /// Gets the median in array.
        /// </summary>
        /// <param name="value">The array of values.</param>
        /// <returns>the median in array</returns>
        public MixedColor GetMedian(MixedColor[]value)
        {
            return this.RandomSelection(0, value.Length - 1, (int)Math.Ceiling(value.Length / 2.0), value);
        }

        /// <summary>
        /// Gets the mid point in array.
        /// </summary>
        /// <param name="value">The value of array.</param>
        /// <param name="result">The result such than first index max and second index min.</param>
        public void GetMidPoint(MixedColor[] value, MixedColor[] result)
        {
            this.GetMinAndMax(value);
            result[0] = this.max;
            result[1] = this.min;
        }

        /// <summary>
        /// get value of subjective index.
        /// </summary>
        /// <param name="start">The start of array.</param>
        /// <param name="end">The end of array.</param>
        /// <param name="indexRequire">The number of value require in array.</param>
        /// <param name="bitMixArray">The array of values.</param>
        /// <returns>the value of index require</returns>
        private MixedColor RandomSelection(int start, int end, int indexRequire, MixedColor[] bitMixArray)
        {
            int indexForSort = this.Partition(start, end, bitMixArray);
               if (indexForSort + 1 == indexRequire)
               {
                   return bitMixArray[indexForSort];
               }
               else if (indexRequire < indexForSort + 1)
               {
                   return this.RandomSelection(start, indexForSort, indexRequire, bitMixArray);
               }
               else
               {
                   return this.RandomSelection(indexForSort + 1, end, indexRequire, bitMixArray);
               }
        }

        /// <summary>
        /// Partitions array to array with max value and array of min value.
        /// </summary>
        /// <param name="start">The start of array.</param>
        /// <param name="end">The end of array.</param>
        /// <param name="bitMixArray">The array of values.</param>
        /// <returns>index of sperate between two array</returns>
        private int Partition(int start, int end, MixedColor[] bitMixArray)
        {
            int i = start + 1;
            int j = end;
            MixedColor pivot = bitMixArray[start];
            while (true)
            {
                while (bitMixArray[i].Value < pivot.Value && i < end)
                {
                    i++;
                }

                while (bitMixArray[j].Value >= pivot.Value && j > start) 
                {
                    j--;
                }

                if (i < j)
                {
                    this.Swap(i, j, bitMixArray);
                }
                else
                {
                    this.Swap(start, j, bitMixArray);
                    return j;
                }
            }
        }

        /// <summary>
        /// Swaps two values
        /// </summary>
        /// <param name="i">the index of first value.</param>
        /// <param name="j">the index of second value.</param>
        /// <param name="bitMixArray">The array of values.</param>
        private void Swap(int i, int j, MixedColor[] bitMixArray)
        {
            if (i != j)
            {
                MixedColor temp = bitMixArray[i];
                bitMixArray[i] = bitMixArray[j];
                bitMixArray[j] = temp;
            }
        }
    }
}
